#import "StarShipView.h"
#import "NewSpaceView.h"
#import "Thinker.h"
#import <stdio.h>
#import "StarShipProtocol.h"
#import <dpsclient/wraps.h>


#define sliderDelay	2000

@implementation StarShipView

static char **bundleFilenameList;


+ initialize
{
    if ( self == [StarShipView class] )
	{
       static NXDefaultsVector starShipDefs = {
	    {"StarShipObjectSpeed",		"180"},
		{"StarShipIntervalTime",	"5"},
	    {"StarShipAnimationSpeed",	"500"},
		{"StarShipCycleCount",	"250"},
		{"StarShipSound",	"1"},
		{"StarShipStarSpeed",	"1"},
	    {NULL}
       };

       NXRegisterDefaults([NXApp appName], starShipDefs);
    }
    return self;
}

- oneStep
{
    if (viewScreenResized){ //tell everybody about it
		if(controllerTypeCount > 0){
		    [spaceTypeController setBoundsRect: &newViewScreenSize];
			//set new clip area
			NXSetRect(&clipRect,newViewScreenSize.origin.x,
			 newViewScreenSize.origin.y,newViewScreenSize.size.width,
			 newViewScreenSize.size.height);
			 // numbers have to do with size of the viewscreen border
			clipRect.origin.x += 3.0;
			clipRect.origin.y += 3.0;;
			clipRect.size.width -= 6.0;
			clipRect.size.height -= 6.0;
			rectPtr = &clipRect;
		}
		viewScreenResized = NO;		

	}
	if (controllerTypeCount > 0){   // they exist
		NXRectClip(rectPtr);  //clip to screen border
		if ([spaceTypeController doUntilDone]){
			[spaceTypeController freeResources];
			controllerIndex++;
			if (controllerIndex >= controllerTypeCount)
				controllerIndex = 0;
			spaceTypeController =  [controllerList
			objectAt:controllerIndex];
			if(soundGood){
				[spaceTypeController setPwrDownSnd:pwrDownSnd];
				[spaceTypeController setPwrUpSnd:pwrUpSnd];
			}
			[self setModuleBox];  //update module info in inspector window	

			[viewScreen setBodyControllerOutlet:spaceTypeController];
			[spaceTypeController setStarsOutlet:viewScreen];
			//send module all slider values
			firstTimeSound = 1;  //don't want to play sound at startup
			[self setSound:soundMatrix];
			[self setStarSpeed:starSpeedSlider];
			[self setAnimationSpeed:animationSlider];
			[self setObjectInterval:objectIntervalSlider];
			[self setObjectSpeed:objectSpeedSlider];
			[self setCycleCount:cycleCountSlider];
			[spaceTypeController setBoundsRect: &newViewScreenSize];
			[spaceTypeController setFirstState];

			
		}
		PSinitclip();    //turn off clipping
	}
	
	[viewScreen oneStep];

    if(frameRate > 1)
	    xmach_sleep(port, frameRate);

	return self;

}
- (BOOL)makeSndObjects: (char *)directory
{
char buf[MAXPATHLEN + 1];
	
	soundGood = YES;
	sprintf(buf,"%s/pwrDown.snd",directory);
	pwrDownSnd = [[Sound alloc] initFromSoundfile:buf];
	if(pwrDownSnd == nil){
		soundGood = NO;
		printf("unable to create sound object %s\n",buf);
	}
	sprintf(buf,"%s/pwrUp.snd",directory);
	pwrUpSnd = [[Sound alloc] initFromSoundfile:buf];
	if(pwrUpSnd == nil){
		soundGood = NO;
		printf("unable to create sound object %s\n",buf);
	}
	sprintf(buf,"%s/sound.snd",directory);
	sound = [[Sound alloc] initFromSoundfile:buf];
	if(sound == nil){
		soundGood = NO;
		printf("unable to create sound object %s\n",buf);
	}
	return soundGood;
}

//*********************************
// this builds a list of bundles in the StarShipView.BackModule bundle
// and instantiates first bundle object
// there must be a class object that is same as bundle name
// The idea here is build a list of space object controllers
// like celestial body controllers, ship controllers, etc
 
- buildControllerList: (char *)directory
{
NXBundle * tmpBundle;
char buf[MAXPATHLEN + 1];	
short ii,successCount;
    
    controllerTypeCount = [self searchDirectory: directory];
    controllerList = [[List alloc] initCount:controllerTypeCount];
	
	successCount = 0;
	if(controllerTypeCount > 0){//there are some
    	successCount = 0;
    	for(ii = 0; ii < controllerTypeCount; ii++){
        	sprintf(buf,"%s/%s.bundle",directory,bundleFilenameList[ii]);

    		if((tmpBundle = [[NXBundle alloc] initForDirectory:buf]) == nil)
				printf("could not create bundle object for %s\n",buf);
			else {
	    		if((spaceTypeController = [[[tmpBundle
				 classNamed:bundleFilenameList[ii]]alloc]init])){
			    	successCount++;
					[controllerList addObject:spaceTypeController];
				}
			}
       
    	}
	controllerTypeCount = successCount;
	}         
    return self;
}
	
static BOOL isOk(const char *filename)
/* checks to make sure the filename is .bundle
 */
{ 
char *suffix;

    suffix = rindex(filename,'.');
    if (suffix == 0)
    	return NO;
    else if(strlen(suffix) != 7)
        return NO;
    else
        return(strncmp(suffix,".bundle",7)) ? NO : YES;  

}

#define CHUNK 127
static char **addFile(const char *file, int length, char **list, int count)
/* Adds the specified filename to the list of filenames.  It allocates 
 * more memory in chunks as needed.
 */
{
    char *suffix;
   
    if (!list) list = (char **)malloc(CHUNK*sizeof(char *));
    if (suffix = rindex(file,'.')) 
        *suffix  = '\0'; 	/* strip .bundle suffix */
    list[count] = (char *)malloc((length+1)*sizeof(char));
    strcpy(list[count], file);
    count++;
    if (!(count% CHUNK)) {
	list = (char **)realloc(list,(((count/CHUNK)+1)*CHUNK)*sizeof(char *));
    }
    list[count] = NULL;
    return list;
}

static void freeList(char **list)
/* Frees the array of filenames
 */
 {
    char **strings;
    if (list) {
	strings = list;
	while (*strings) free(*strings++);
	free(list);
    }
}


// look in the right bundle directory for all the space object bundles
// create a list called bundleFilenameList
- (int)searchDirectory: (char *)directory

{
    long basep;
    char *buf;
    struct direct *dp;
    char **list = NULL;
    int cc, fd, fileCount = 0;
    char dirbuf[8192];

    if ((fd = open(directory, O_RDONLY, 0644)) > 0) {
	cc = getdirentries(fd, (buf = dirbuf), 8192, &basep);
	while (cc) {
	    dp = (struct direct *)buf;
	    
	    if (isOk(dp->d_name)) {
		list = addFile(dp->d_name, dp->d_namlen, list, fileCount++);
	    }
	    buf += dp->d_reclen;
	    if (buf >= dirbuf + cc) {
		cc = getdirentries(fd, (buf = dirbuf), 8192, &basep);
	    }
	}
	close(fd);
    }

    freeList(bundleFilenameList);
    bundleFilenameList = list;
    return fileCount;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	[super sizeTo:width :height];

	[viewScreen sizeTo:width :height];//send original size to space
	
	if(controllerTypeCount > 0){
		[spaceTypeController setBoundsRect:&newViewScreenSize];
		NXSetRect(&clipRect,newViewScreenSize.origin.x,
			newViewScreenSize.origin.y,
			newViewScreenSize.size.width,newViewScreenSize.size.height);
		clipRect.origin.x += 3.0;
		clipRect.origin.y += 3.0;;
		clipRect.size.width -= 6.0;
		clipRect.size.height -= 6.0;
		rectPtr = &clipRect;
		
	[spaceTypeController windowSizeChanged];	
	
	}
			
	return self;
}



- initFrame:(const NXRect *)frameRect
{
char buf[MAXPATHLEN + 1];

	[super initFrame:frameRect];
	[self allocateGState];		// For faster lock/unlockFocus
	[self inspector:self];      //this guarantees that sliders exist when I
								//set them	
	moduleDir = [[NXApp delegate] moduleDirectory:"StarShip"];
	strcpy(buf, moduleDir);
	

	viewScreen = [[NewSpaceView alloc] initFrame:frameRect];
	[viewScreen setStarShipOutlet:self];

	NXSetRect(&clipRect,newViewScreenSize.origin.x,
	 newViewScreenSize.origin.y,
	 newViewScreenSize.size.width,newViewScreenSize.size.height);
	clipRect.origin.x += 3.0;
	clipRect.origin.y += 3.0;;
	clipRect.size.width -= 6.0;
	clipRect.size.height -= 6.0;
	rectPtr = &clipRect;

	
	// builds list of bundle objects
	[self buildControllerList:buf];
	controllerIndex = 0;
	spaceTypeController =  [controllerList objectAt:controllerIndex];
	if([self makeSndObjects:buf]){
		[spaceTypeController setPwrDownSnd:pwrDownSnd];
		[spaceTypeController setPwrUpSnd:pwrUpSnd];
	}
	[self setModuleBox];  //update module info in inspector window	
	
	[spaceTypeController setBoundsRect: (NXRect *)frameRect];

	[spaceTypeController setStarsOutlet:viewScreen];


	[viewScreen setBodyControllerOutlet:spaceTypeController];

	[self getDefaults:self];
	firstTimeSound = 1;  //don't want to play sound at startup
	[self setSound:soundMatrix];
	[self setStarSpeed:starSpeedSlider];
	[self setAnimationSpeed:animationSlider];
	[self setObjectInterval:objectIntervalSlider];
	[self setObjectSpeed:objectSpeedSlider];
	[self setCycleCount:cycleCountSlider];
	[spaceTypeController setFirstState];
	
	port = xmach_sleep_install();
	viewScreenResized = NO;
	
	return self;
}
- setCycleCount:(Slider *)sender
{
int	multiplier,value;;

	// real slider value min is 0
	//I show a min of 1 cycle in the cycleCountBox
	value = [sender intValue];
	multiplier = (int)[spaceTypeController setCycleValue:value];
	if (value == 0)
		value = 1;
	else
		value *= multiplier;
	[cycleCountBox setIntValue:value];

	[self delayedDefaults:self];
	return self;
}
- setStarSpeed:(Slider *)sender
{
	[viewScreen setStarSpeed:sender];
	[spaceTypeController setStarSpeed:sender];
	[self delayedDefaults:self];

	return self;
}

- setAnimationSpeed:(Slider *)sender
{
	frameRate = ([sender maxValue] + [sender minValue]) - [sender intValue];
	[self delayedDefaults:self];

	return self;
}
- setObjectSpeed:(Slider *)sender
{
	[spaceTypeController setObjectSpeed:sender];
	[self delayedDefaults:self];

	return self;
}
- setObjectInterval:(Slider *)sender
{
	[spaceTypeController setStartInterval: sender];//send slider to current
	[self delayedDefaults:self];			 		//space object
	return self;
}
- setSound:sender
{

	if(soundGood){  //there are valid sound files
		if(!firstTimeSound)
			[sound play];
		firstTimeSound = 0;
		soundEnabled = (BOOL)[[sender selectedCell] tag];
		[spaceTypeController setSoundEnabled:soundEnabled];
	}
	else{  //set all sound off permanently
		soundEnabled = NO;
		[spaceTypeController setSoundEnabled:soundEnabled];
	
	}
	[self delayedDefaults:self];
	
	return self;
}
- setModuleBox
{
	[moduleBox setStringValue:bundleFilenameList[controllerIndex]];
	return self;
}


- didLockFocus
{
	[viewScreen didLockFocus];
	return self;
}

- (const char *) windowTitle
{
	return("StarShip!");
}

- inspector:sender
{
    char buf[MAXPATHLEN];
	
    if (!inspectorPanel)
	{
		sprintf(buf,"%s/English.lproj/StarShip.nib",[[NXApp delegate]
		moduleDirectory:"StarShip"]);
		if([NXApp loadNibFile:buf owner:self withNames:NO] == nil)
		    printf ("inspector not loaded %s\n",buf);
    }
    return inspectorPanel;
}

- inspectorWillBeRemoved
{
	[[infoText window] orderOut:self];
	return self;
}

// The module is never freed
// but it's here in case backSpace ever wants to ;-)

- free
{
	//This is untested!!!!
	
	[controllerList freeObjects];
	[controllerList free];
	[pwrDownSnd free];
	[pwrUpSnd free];
	[sound free];
	freeList(bundleFilenameList);
	xmach_sleep_remove(port);
	return [super free];
}


// I left this in in case some of the modules wanted to do 
//something cool with mouse
//down events  - it is not functional right now

/*- mouseDown:(NXEvent *)theEvent;
{
	NXRect			r;
	NXPoint			p;
	unsigned int	i;

	p = theEvent->location;
	[self convertPoint:&p fromView:nil];

	[self lockFocus];
	[spaceTypeController doSomethingNow];
	[self unlockFocus];

	return self;
}*/
//needed for mouse down events
/*- newWindow
{
	shipWindow = [self window];
	[shipWindow addToEventMask:NX_LMOUSEDOWNMASK];
	return self;
}*/

- delayedDefaults:sender
{
	[self perform:@selector(writeDefaults:) with:self afterDelay:sliderDelay
	 cancelPrevious:YES];
	return self;
}


- getDefaults:sender
{
int cell;
	[animationSlider setIntValue:atoi(NXGetDefaultValue([NXApp appName],
	 "StarShipAnimationSpeed"))];
	
	[objectSpeedSlider setIntValue:atoi(NXGetDefaultValue([NXApp appName],
	 "StarShipObjectSpeed"))];
	
	[starSpeedSlider setFloatValue:atof(NXGetDefaultValue([NXApp appName],
	 "StarShipStarSpeed"))];


	[objectIntervalSlider setIntValue:atoi(NXGetDefaultValue([NXApp appName],
	 "StarShipIntervalTime"))];
	
	[cycleCountSlider setIntValue:atoi(NXGetDefaultValue([NXApp appName],
	 "StarShipCycleCount"))];

	[cycleCountBox setIntValue:atoi(NXGetDefaultValue([NXApp appName],
	 "StarShipCycleCount"))];
	
	
	cell = atoi(NXGetDefaultValue([NXApp appName], "StarShipSound"));
	if(cell)
		[soundMatrix selectCellAt:0:0];
	else
		[soundMatrix selectCellAt:1:0];
	return self;
}

- writeDefaults:sender
{
int tag;
char ctag[2];

	NXWriteDefault([NXApp appName], "StarShipAnimationSpeed",
	 [animationSlider stringValue]);
	
	NXWriteDefault([NXApp appName], "StarShipObjectSpeed",
	 [objectSpeedSlider stringValue]);

	NXWriteDefault([NXApp appName], "StarShipStarSpeed",
	 [starSpeedSlider stringValue]);
	
	NXWriteDefault([NXApp appName], "StarShipIntervalTime",
	 [objectIntervalSlider stringValue]);
	
	NXWriteDefault([NXApp appName], "StarShipCycleCount",
	 [cycleCountSlider stringValue]);
	
	tag = [[soundMatrix selectedCell] tag];
	sprintf(ctag,"%d",tag);
	NXWriteDefault([NXApp appName], "StarShipSound",ctag);
	return self;
}

port_t *xmach_sleep_install(void)
{
	port_t			*port;

	NX_MALLOC(port, port_t, sizeof(port_t));
	port_allocate(task_self(), port);
	
	return(port);
}

void xmach_sleep_remove(port_t *port)
{
	port_deallocate(task_self(), *port);
	NX_FREE(port);
}

void xmach_sleep(port_t *port, msg_timeout_t msecs)
{
	msg_header_t	h;

	h.msg_size	= sizeof(h);
	h.msg_local_port = *port;

	msg_receive(&h, RCV_TIMEOUT, msecs);
}
- viewScreenResized:(NXRect *)screenRect
{
	viewScreenResized = YES;
	NXSetRect(&newViewScreenSize,screenRect->origin.x,
	screenRect->origin.y,screenRect->size.width, 
	screenRect->size.height);
	return self;
}
@end


